﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Core;
using System.Reflection;

namespace Core.LockStep
{
    public interface ILockStepHelper : IHelperTag
    {
        void AnalysisGameAssembly(Assembly gameAssembly);
        float Interpolation { get; set; }
        int GetStepDataId(Type type);
        Type GetStepDataType(int stepId);
        int GetBackupDataId(Type type);
        Type GetBackupDataType(int backupId);
        void AddServerParseStepData(IStreamData stepData);
        void ClearServerParseData();
        IDataEnumerable<T> GetStepDatas<T>() where T : IStepData;
        IDataEnumerable<T> GetBackupDatas<T>() where T : IBackupData;
        T CreateBackupData<T>(int backupId) where T : IBackupData, new();
        T GetBackupData<T>(int backupId) where T : IBackupData;
        void ClearBackupDatas();
        LockstepInfoData CreateLockstepInfoData();
        void SetAllBackupDataInterpolationEnd();
        void Backup(int frameIndex);
        void Rollback(int frameIndex);
        void RemoveRollbackData(int frameIndex);
        void Reset();
        FrameDataStream GetNeedSendFrameData(int frameIndex, int playerId);
        IStepData GetStepDataFromStream(FrameDataStream frameData);
        void AddNeedSendStepData(IStepData stepData);
    }

    public class LockStepHelper : Helper, ILockStepHelper
    {
        private Dictionary<int, Type> stepId2Type = new Dictionary<int, Type>();
        private Dictionary<Type, int> stepType2Id = new Dictionary<Type, int>();

        private Dictionary<int, Type> backupId2Type = new Dictionary<int, Type>();
        private Dictionary<Type, int> backupType2Id = new Dictionary<Type, int>();

        // 当前帧需要发送的数据（自己的输入数据）
        private List<IStepData> currentFrameNeedSendStepDatas = new List<IStepData>();

        private Type lockstepInfoType;

        // 当前逻辑帧解析出来的数据
        private Dictionary<Type, IDataContainer> serverParseStepDatas = new Dictionary<Type, IDataContainer>();

        // 所有需要进行快照备份的数据
        private Dictionary<Type, IBackupDataContainer> backupDatas = new Dictionary<Type, IBackupDataContainer>();

        private int minBackupIndex;
        private Dictionary<int, RollbackChunk> rollbackChunks = new Dictionary<int, RollbackChunk>();

        public float Interpolation { get; set; }

        public override void OnInit()
        {
            LockStepTool.helper = this;
        }

        public override void OnDestroy()
        {
            LockStepTool.helper = null;
        }

        public void AnalysisGameAssembly(Assembly gameAssembly)
        {
            var types = gameAssembly.GetTypes();
            foreach (var dataType in types)
            {
                if (TrySetLockstepInfoType(dataType))
                    continue;

                if (TrySetStepDataType(dataType))
                    continue;

                if (TrySetBackupDataType(dataType))
                    continue;
            }
        }

        private bool TrySetLockstepInfoType(Type dataType)
        {
            if (lockstepInfoType == null)
            {
                var lockstepAtt = dataType.GetCustomAttribute(typeof(LockstepInfoAttribute)) as LockstepInfoAttribute;
                if (lockstepAtt != null)
                {
                    lockstepInfoType = dataType;
                    return true;
                }
            }

            return false;
        }

        private bool TrySetStepDataType(Type dataType)
        {
            var att = dataType.GetCustomAttribute(typeof(StepDataIDAttribute)) as StepDataIDAttribute;
            if (att != null)
            {
                if (stepId2Type.TryGetValue(att.id, out var type))
                {
                    Log.Error($"已存在相同数据的ID，请重新设置ID。已存在：{type}, 尝试添加的：{dataType.FullName}");
                    return true;
                }

                stepType2Id.Add(dataType, att.id);
                stepId2Type.Add(att.id, dataType);
                return true;
            }

            return false;
        }

        private bool TrySetBackupDataType(Type dataType)
        {
            var att = dataType.GetCustomAttribute(typeof(BackupDataIdAttribute)) as BackupDataIdAttribute;
            if (att != null)
            {
                if (backupId2Type.TryGetValue(att.id, out var type))
                {
                    Log.Error($"已存在相同数据的ID，请重新设置ID。已存在：{type}, 尝试添加的：{dataType.FullName}");
                    return true;
                }

                backupType2Id.Add(dataType, att.id);
                backupId2Type.Add(att.id, dataType);

                return true;
            }

            return false;
        }

        public int GetStepDataId(Type type)
        {
            if (stepType2Id.TryGetValue(type, out var id))
                return id;

            Log.Error($"未找到类型id:{type}");
            return 0;
        }

        public Type GetStepDataType(int stepId)
        {
            if (stepId2Type.TryGetValue(stepId, out var stepType))
                return stepType;

            Log.Error($"未找到操作id类型:{stepId}");
            return null;
        }


        public int GetBackupDataId(Type type)
        {
            if (backupType2Id.TryGetValue(type, out var id))
                return id;

            Log.Error($"未找到类型id:{type}");
            return 0;
        }

        public Type GetBackupDataType(int backupId)
        {
            if (backupId2Type.TryGetValue(backupId, out var backupType))
                return backupType;

            Log.Error($"未找到备份id类型:{backupId}");
            return null;
        }

        public LockstepInfoData CreateLockstepInfoData()
        {
            if (lockstepInfoType == null)
                return null;

            return (LockstepInfoData)Activator.CreateInstance(lockstepInfoType);
        }

        public void AddServerParseStepData(IStreamData stepData)
        {
            var type = stepData.GetType();
            if (!serverParseStepDatas.TryGetValue(type, out var container))
            {
                var containerType = typeof(DataContainer<>);
                containerType = containerType.MakeGenericType(type);
                container = (IDataContainer)Activator.CreateInstance(containerType);
                serverParseStepDatas[type] = container;
            }

            container.AddData(stepData);
        }

        public void ClearServerParseData()
        {
            foreach (var container in serverParseStepDatas.Values)
                container.Clear();
        }

        public IDataEnumerable<T> GetStepDatas<T>() where T : IStepData
        {
            var type = typeof(T);
            if (!serverParseStepDatas.TryGetValue(type, out var container))
            {
                container = new DataContainer<T>();
                serverParseStepDatas[type] = container;
            }

            return ((DataContainer<T>)container).GetEnumerable();
        }

        public T CreateBackupData<T>(int backupId) where T : IBackupData, new()
        {
            var type = typeof(T);
            if (!backupDatas.TryGetValue(type, out var container))
            {
                container = new BackupDataContainer<T>();
                backupDatas[type] = container;
            }

            var result = PoolTools.Pop<T>();
            result.InstanceId = backupId;
            container.AddData(result);

            return result;
        }

        public IDataEnumerable<T> GetBackupDatas<T>() where T : IBackupData
        {
            var type = typeof(T);
            if (!backupDatas.TryGetValue(type, out var container))
            {
                container = new BackupDataContainer<T>();
                backupDatas[type] = container;
            }

            return ((DataContainer<T>)container).GetEnumerable();
        }

        public T GetBackupData<T>(int backupId) where T : IBackupData
        {
            var type = typeof(T);
            if (!backupDatas.TryGetValue(type, out var container))
                return default;
            return (T)container.GetData(backupId);
        }

        public void SetAllBackupDataInterpolationEnd()
        {
            foreach (var item in backupDatas.Values)
            {
                var enumerator = item.GetDataEnumerator();
                while (enumerator.MoveNext())
                {
                    ((IInterpolationData)enumerator.current).InterpolationEnd();
                }
            }
        }

        public void ClearBackupDatas()
        {
            foreach (var container in backupDatas.Values)
                container.Clear();
        }

        public void Backup(int frameIndex)
        {
            if (rollbackChunks.ContainsKey(frameIndex))
                return;

            var rollbackChunk = PoolTools.Pop<RollbackChunk>();

            rollbackChunk.SeekBegin();

            foreach (var item in backupDatas.Values)
            {
                var enumerator = item.GetDataEnumerator();
                while (enumerator.MoveNext())
                {
                    var current = (IBackupData)enumerator.current;

                    // 先写入类型ID
                    var backupId = GetBackupDataId(current.GetType());
                    rollbackChunk.WriteInt32(backupId);

                    // 写入实例ID
                    rollbackChunk.WriteInt32(current.InstanceId);

                    // 在写入数据
                    current.WriteTo(rollbackChunk);
                }
            }

            rollbackChunks[frameIndex] = rollbackChunk;
        }

        public void Rollback(int frameIndex)
        {
            try
            {
                // 先清除当前备份数据
                ClearBackupDatas();

                TryReadRollbackData(frameIndex);
            }
            catch (Exception e)
            {
                Log.Error($"解析数据失败，清空当前备份数据。{e}");

                ClearBackupDatas();
            }

            // 触发回滚后，后面的回滚数据都是不同步的了，直接清除
            ClearRollBackDatas();
        }

        public void TryReadRollbackData(int frameIndex)
        {
            // 从回滚块中还原数据

            if (!rollbackChunks.TryGetValue(frameIndex, out var frameData))
                return;

            frameData.SeekBegin();

            while (frameData.Position < frameData.Length)
            {
                // 数据编号
                var backupId = frameData.ReadInt32();
                var backupType = GetBackupDataType(backupId);

                // 读取实例ID
                var instanceId = frameData.ReadInt32();

                if (!backupDatas.TryGetValue(backupType, out var container))
                    throw new Exception("读备份数据失败");

                var result = (IBackupData)PoolTools.Pop(backupType);
                result.InstanceId = instanceId;

                // 数据
                result.ReadFrom(frameData);

                container.AddData(result);
            }
        }

        public void RemoveRollbackData(int frameIndex)
        {
            // 小于指定索引的都清除
            var min = minBackupIndex;
            for (int i = min; i <= frameIndex; i++)
            {
                if (rollbackChunks.TryGetValue(i, out var rollbackChunk))
                {
                    PoolTools.Push(rollbackChunk);
                    minBackupIndex = i;
                    rollbackChunks.Remove(i);
                }
            }
        }

        private void ClearRollBackDatas()
        {
            foreach (var rollbackChunk in rollbackChunks.Values)
                PoolTools.Push(rollbackChunk);
            rollbackChunks.Clear();
        }

        public IStepData GetStepDataFromStream(FrameDataStream frameData)
        {
            // 数据编号
            var stepDataId = frameData.ReadInt32();
            var stepDataType = GetStepDataType(stepDataId);

            var stepData = (IStepData)PoolTools.Pop(stepDataType);

            // 数据
            stepData.ReadFrom(frameData);

            return stepData;
        }

        public void AddNeedSendStepData(IStepData stepData)
        {
            currentFrameNeedSendStepDatas.Add(stepData);
        }

        public FrameDataStream GetNeedSendFrameData(int frameIndex, int playerId)
        {
            var stream = PoolTools.Pop<FrameDataStream>();

            // 预留长度
            stream.WriteInt32(0);
            // 写入玩家Id
            stream.WriteInt32(playerId);

            // 当前帧数据
            foreach (var item in currentFrameNeedSendStepDatas)
            {
                try
                {
                    WriteStepDataToStream(item, stream);
                }
                catch (Exception e)
                {
                    Log.Error($"写入失败，丢弃第{frameIndex}帧。\n{e}");

                    stream.Clear();
                    break;
                }
            }
            currentFrameNeedSendStepDatas.Clear();

            // 返回开始，重新写入长度
            stream.SeekBegin();
            stream.WriteInt32(stream.Length);

            return stream;
        }

        private void WriteStepDataToStream(IStepData stepData, IFrameDataStream stream)
        {
            // 写入数据编号(int)
            var stepDataId = GetStepDataId(stepData.GetType());
            stream.WriteInt32(stepDataId);
            // 写入数据
            stepData.WriteTo(stream);
        }

        public void Reset()
        {
            Interpolation = 0;
            minBackupIndex = 0;
            ClearServerParseData();
            ClearBackupDatas();
            ClearRollBackDatas();

            foreach (var item in currentFrameNeedSendStepDatas)
                PoolTools.Push(item);
            currentFrameNeedSendStepDatas.Clear();

            PoolTools.RecycleAllReferences();
        }
    }
}